package com.example.mall.common.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.mall.common.core.exception.UnauthorizedException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;

@Component
public class JwtToken {
    private static String jwtKey;
    private static Integer tokenExpiredIn;
    // 系统设置的用户默认vip级别
    // 客户端的角色等级标识，预留字段, 未来可拓展vip系统。
    private static Integer userDefaultScope;

    @Value("${app.security.jwt-key}")
    public void setJwtKey(String jwtKey) {
        JwtToken.jwtKey = jwtKey;
    }

    @Value("${app.security.token-expired-in}")
    public void setTokenExpiredIn(Integer tokenExpiredIn) {
        JwtToken.tokenExpiredIn = tokenExpiredIn;
    }

    @Value("${app.userDefaultScope}")
    public void setUserDefaultScope(Integer scope) {
        JwtToken.userDefaultScope = scope;
    }

    /**
     * 生成令牌
     * @param userId 用户id
     * @param scope 用户vip-标识
     * @return
     */
    public static String generalToken(Long userId, Integer scope) {
        return getToken(userId, scope);
    }

    /**
     * 根据用户信息-生成令牌
     * @param userId 用户id
     * @return
     */
    public static String generalToken(Long userId) {
        return getToken(userId, userDefaultScope);
    }

    /**
     * 令牌生成器
     * @param userId
     * @param scope
     * @return
     */
    private static String getToken(Long userId, Integer scope) {
        Algorithm algorithm = Algorithm.HMAC256(JwtToken.jwtKey);
        String token = JWT.create()
                .withClaim("uid", userId)
                .withClaim("scope", scope)
                .withExpiresAt(JwtToken.calutExpireAt().get("expiredTime"))
                .withIssuedAt(JwtToken.calutExpireAt().get("now"))
                .sign(algorithm);
        return token;
    }

    /**
     * 计算出令牌的过期时间
     *  - now 令牌生成的时间
     *  - expiredTime 令牌过期的时间
     *  - tokenExpiredIn 有效的时长
     * @return
     */
    private static Map<String, Date> calutExpireAt() {
        Map<String, Date> map = new HashMap<>();
        Calendar calendar = Calendar.getInstance();
        Date now = calendar.getTime();
        calendar.add(Calendar.SECOND, JwtToken.tokenExpiredIn);
        // 当前的时间
        map.put("now", now);
        // 过期的时间
        map.put("expiredTime", calendar.getTime());
        return map;
    }

    /**
     * 获取jwt校验器
     * @param jwtKey 密钥
     * @return
     */
    private static JWTVerifier getJWTVerifier(String jwtKey) {
        Algorithm algorithm = Algorithm.HMAC256(jwtKey);
        JWTVerifier verifier = JWT.require(algorithm).build();
        return verifier;
    }

    /**
     * 校验令牌信息
     * @param token
     * @return 返回令牌的信息
     */
    public static Optional<Map<String, Claim>> verifyToken(String token) {
        if ("".equals(token)) {
            throw new UnauthorizedException(10004);
        }

        // 根据加密算法 解码 token
        JWTVerifier jwtVerifier = getJWTVerifier(JwtToken.jwtKey);
        Map<String, Claim> claims = new HashMap<>();

        try {
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            claims = decodedJWT.getClaims();
        } catch(JWTVerificationException e) {
            // 校验失败
            // 需要根据过期时间,与当前时间进行比对,判断令牌是否过期、
            System.out.println("校验失败...");
            e.printStackTrace();
            return Optional.empty();
        }

        return Optional.of(claims);
    }

    /**
     * 令牌快速校验
     *  - 令牌是否有效
     * @param token
     * @return true -> 有效, false -> 无效
     */
    public static boolean checkToken(String token) {
        if ("".equals(token)) {
            throw new UnauthorizedException(10004);
        }
        JWTVerifier jwtVerifier = getJWTVerifier(JwtToken.jwtKey);

        try {
            DecodedJWT decodedJWT = jwtVerifier.verify(token);

            return true;
        } catch (JWTVerificationException e) {
            return false;
        }
    }
}
